
#include "variantExtensions.h"

/****************************************************************************************
*
*	MathLink GetValue Extension
*
****************************************************************************************/

bool MLGetValue(MLINK mlp, bool* value){
	const char* sym;
	bool result = MLGetSymbol(mlp,&sym) != 0;
	if(string(sym) == string("True"))
		*value = true;
	else if(string(sym) == string("False"))
		*value = false;
	else
		result = false;
	MLReleaseSymbol(mlp,sym);
	return result;
}

bool MLGetValue(MLINK mlp, size_t* value){
	long val; 
	bool result = MLGetLongInteger(mlp,&val) != 0;
	*value = val;
	return result;
}

bool MLGetValue(MLINK mlp, long* value){
	return MLGetLongInteger(mlp,value) != 0;
}


bool MLGetValue(MLINK mlp, double* value){
	return MLGetDouble(mlp,value) != 0;
}

bool MLGetValue(MLINK mlp, complex* value){
	int args;
	bool result = (MLTestHead(mlp,"Complex",&args) != 0 && (args == 2));
	if(result)
	{
		double r,i;

		result = MLGetDouble(mlp,&r) != 0;
		result &= MLGetDouble(mlp,&i) != 0;

		*value = complex(r,i);
	}
	return result;
}

bool MLGetValue(MLINK mlp, numeric* value){
	
	long integer;
	double doub;
	complex cx;
	
	MLMARK mark = MLCreateMark(mlp);
	int type = MLGetNext(mlp);
		
	switch(type)
	{
	case MLTKINT:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		if(!MLGetValue(mlp,&integer))
			return false;
		*value = integer;
		return true;
	case MLTKREAL:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		if(!MLGetValue(mlp,&doub))
			return false;
		*value = doub;
		return true;
	case MLTKFUNC:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		if(!MLGetValue(mlp,&cx))
			return false;
		*value = cx;
		return true;
	case MLTKERR:
	case MLTKSTR:
	case MLTKSYM:
	default:
		MLDestroyMark(mlp,mark);
		break;
	}
	
	return false;
}

bool MLGetValue(MLINK mlp, numeric::range* value){
	
	int args;
	bool list = (MLTestHead(mlp,"List",&args) != 0);
	bool span = (list) ? false : (MLTestHead(mlp,"Span",&args) != 0);
	bool result = ( (list || span) && (args == 2 || args == 3));
	if(result)
	{
		result = MLGetValue(mlp,&value->begin) != 0;
		if(!result)
			return false;
		
		result = MLGetValue(mlp,&value->end) != 0;
		if(!result)
			return false;
		else if(args == 2)
			return true;
		
		result = MLGetValue(mlp,&value->step) != 0;
	}
	return result;
}

bool MLGetValue(MLINK mlp, char* value){
	const char* str;
	bool result = (MLGetString(mlp,&str) != 0 && std::strlen(str) != 1);
	if(result)
		*value = str[0];
	MLReleaseString(mlp,str);
	return result;
}

bool MLGetValue(MLINK mlp, string* value){
	const char* str;
	bool result = (MLGetString(mlp,&str) != 0);
	if(result)
		*value = str;
	MLReleaseString(mlp,str);
	return result;
}

bool MLGetValue(MLINK mlp, symbol* value){
	const char* sym;
	bool result = (MLGetSymbol(mlp,&sym) != 0);
	if(result)
	{
		string str = sym;
		if(str == *symbol::all)
			*value = symbol::all;
		else if(str == *symbol::none)
			*value = symbol::none;
		else if(str == *symbol::automatic)
			*value = symbol::automatic;
		else
			*value = symbol(sym);
	}
	MLReleaseSymbol(mlp,sym);
	return result;
}
	
bool MLGetValue(MLINK mlp, numeric::list* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		numeric tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, variant* value);

bool MLGetValue(MLINK mlp, variant::list* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		variant tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, variant* value){

	int args;
	const char* head;

	numeric number;
	numeric::range range;
	symbol sym;
	string str;
	variant::list list;

	MLMARK mark = MLCreateMark(mlp);
	int type = MLGetNext(mlp);
		
	switch(type)
	{
	case MLTKINT:
	case MLTKREAL:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		if(!MLGetValue(mlp,&number))
			return false;
		*value = number;
		return true;
	case MLTKSYM:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		if(!MLGetValue(mlp,&sym))
			return false;
		if(sym.name() == "True")
			*value = true;
		else if(sym.name() == "False")
			*value = false;
		else
			*value = sym;
		return true;
	case MLTKFUNC:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;

		if(!MLGetFunction(mlp,&head,&args))
			return false;

		str = head;

		MLReleaseSymbol(mlp,head);
		
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		
		if(str == "List")
		{
			if(!MLGetValue(mlp,&list))
				return false;
			*value = list;
			return true;
		}
		else if(str == "Span")
		{
			if(!MLGetValue(mlp,&range))
				return false;
			*value = range;
			return true;
		}
		else if(str == "Complex")
		{
			if(!MLGetValue(mlp,&number))
				return false;
			*value = number;
			return true;
		}

		return false;
	case MLTKSTR:
		if(!MLSeekToMark(mlp,mark,0)) // Return to read same expression.
			return false;
		MLDestroyMark(mlp,mark);
		if(!MLGetValue(mlp,&str))
			return false;
		*value = str;
		return true;
	default:
		MLDestroyMark(mlp,mark);
		return false;
	}
	
	return false;
}

bool MLGetValue(MLINK mlp, rule* value){
	int args;
	bool result = (MLTestHead(mlp,"Rule",&args) && args == 2);
	
	if(result)
		result = MLGetValue(mlp,&value->first);
	if(result)
		result = MLGetValue(mlp,&value->second);

	return result;
}

bool MLGetValue(MLINK mlp, rule::list* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		rule tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

/****************************************************************************************
*
*	MathLink PutValue Extension
*
****************************************************************************************/

bool MLPutValue(MLINK mlp, const bool &value){
	if(value)
		return MLPutSymbol(mlp,"True") != 0;
	else
		return MLPutSymbol(mlp,"False") != 0;
}

bool MLPutValue(MLINK mlp, const long &value){
	return MLPutLongInteger(mlp,value) != 0;
}

bool MLPutValue(MLINK mlp, const size_t &value){
	return MLPutLongInteger(mlp,(long)value) != 0;
}

bool MLPutValue(MLINK mlp, const double &value){
	return MLPutReal64(mlp,value) != 0;
}

bool MLPutValue(MLINK mlp, const complex &value){
	bool result = MLPutFunction(mlp,"Complex",2) != 0;
	if(result)
	{
		result = MLPutReal64(mlp,value.real()) != 0;
		result &= MLPutReal64(mlp,value.imag()) != 0;
	}
	return result;
}

bool MLPutValue(MLINK mlp, const numeric &value){
	switch(value.getType())
	{
	case numeric::sizetype:
		return MLPutValue(mlp, (size_t)value);
	case numeric::longtype:
		return MLPutValue(mlp, (long)value);
	case numeric::doubletype:
		return MLPutValue(mlp, (double)value);
	case numeric::complextype:
		return MLPutValue(mlp, (complex)value);
	default:
		break;
	}
	return false;
}

bool MLPutValue(MLINK mlp, const numeric::range &value){
	bool result = MLPutFunction(mlp,"List",3) != 0;
	if(!MLPutValue(mlp,value.begin))
		return false;
	if(!MLPutValue(mlp,value.end))
		return false;
	if(!MLPutValue(mlp,value.step))
		return false;
	return true;
}

bool MLPutValue(MLINK mlp, const numeric::list &value){
	const size_t size = value.size();
	bool result = MLPutFunction(mlp,"List",size) != 0;
	for(size_t v = 0; v < size && result; v++)
		result &= MLPutValue(mlp,value[v]);
	return result;
}

bool MLPutValue(MLINK mlp, const char &value){
	char* str = "c"; str[0] = value;
	return MLPutString(mlp,str) != 0;
}

bool MLPutValue(MLINK mlp, const string &value){
	return MLPutString(mlp,value.c_str()) != 0;
}

bool MLPutValue(MLINK mlp, const symbol* value){
	return MLPutSymbol(mlp,value->name().c_str()) != 0;
}

bool MLPutValue(MLINK mlp, const variant &value);

bool MLPutValue(MLINK mlp, const variant::list &value){
	const size_t size = value.size();
	bool result = MLPutFunction(mlp,"List",size) != 0;
	for(size_t v = 0; v < size && result; v++)
		result &= MLPutValue(mlp,value[v]);
	return result;
}

bool MLPutValue(MLINK mlp, const variant &value){

	switch(value.getType())
	{
	case variant::booltype:
		return MLPutValue(mlp, value.toBool()) != 0;
	case variant::numerictype:
		return MLPutValue(mlp, value.toNumeric()) != 0;
	case variant::rangetype:
		return MLPutValue(mlp, value.toRange()) != 0;
	case variant::stringtype:
		return MLPutValue(mlp, value.toString()) != 0;
	case variant::symboltype:
		return MLPutValue(mlp, value.toSymbol()) != 0;
	case variant::numericlisttype:
		return MLPutValue(mlp, value.toNumericList()) != 0;
	case variant::listtype:
		return MLPutValue(mlp, value.toList()) != 0;
	case variant::empty:
		MLReturnError("GrapheneTools","error","Returned empty result");
		return true;
	}
	
	return false;
}